//
// $Id: AlternateMovesPreferencePane.m 6 2010-08-05 19:43:27Z principiadriver $
//
// Written by
//  Michael Klein <michael.klein@puffin.lb.shuttle.de>
//
// This file is part of Alternate Moves.
//
// This  is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#import "AlternateMovesPreferencePane.h"
#import "../Helper/AlternateMovesRemoteProtocol.h"


// 10.4 compatibility
@interface NSAlert (SuppressionButton)
- (void)setShowsSuppressionButton:(BOOL)flag;
- (NSButton*)suppressionButton;
@end


@implementation AlternateMovesPreferencePane

#define kDomainName     @"de.shuttle.lb.puffin.AlternateMoves"
#define kLoginItemName  @"AlternateMovesHelper"


enum {
    kCheckLoginItemScript,
    kMakeLoginItemScript,
    kDeleteLoginItemScript,
    kCheckUniversalAccessScript,
    kEnableUniversalAccessScript,
    kScriptCount
};


enum ScriptArg {
    kNoArg, kHelperPath
};


static struct {
    enum ScriptArg arg;
    NSString *source;
} scriptSources[] = {
    kNoArg, @"tell application \"System Events\" to return login item \"" kLoginItemName "\" exists",
    kHelperPath, @"tell application \"System Events\" to make login item at end with properties {path:\"%@\", name:\"" kLoginItemName "\", kind:\"application\"}",
    kNoArg, @"tell application \"System Events\" to delete login items whose name is \"" kLoginItemName "\"",
    kNoArg, @"tell application \"System Events\" to return UI Elements enabled",
    kNoArg, @"tell application \"System Events\" to set UI Elements enabled to true"
};


- (void)mainViewDidLoad
{
    NSBundle *bundle = [self bundle];

    NSDictionary *dict = [bundle infoDictionary];
    NSString *version = [NSString stringWithFormat:[prefsAboutVersion stringValue],
                         [dict objectForKey:@"CFBundleName"],
                         [dict objectForKey:@"CFBundleVersion"]];

    [universalAccessWarningImage setImage:[[NSWorkspace sharedWorkspace]
                                           iconForFileType:NSFileTypeForHFSTypeCode(kAlertCautionIcon)]];

    NSString *path = [[self bundle] pathForResource:@"Defaults" ofType:@"plist"];
    NSDictionary *defaults = [NSDictionary dictionaryWithContentsOfFile:path];
    [[NSUserDefaults standardUserDefaults] setVolatileDomain:defaults forName:kDomainName];

    [prefsAboutVersion setStringValue:version];
    [prefsAboutText readRTFDFromFile:[bundle pathForResource:@"About" ofType:@"rtf"]];

    [startStopButton setTarget:self];

    NSMutableArray *a = [NSMutableArray arrayWithCapacity:kScriptCount];

    int i;
    for (i = 0; i < kScriptCount; i++) {
        NSString *source;
        switch (scriptSources[i].arg) {
            case kNoArg:
                source = scriptSources[i].source;
                break;

            case kHelperPath:
                source = [NSString stringWithFormat:scriptSources[i].source, [self helperPath]];
                break;

            default:
                source = nil;
        }

        if (source) {
            NSDictionary *errorInfo;
            NSAppleScript *as = [[NSAppleScript alloc] initWithSource:source];
            if (![as compileAndReturnError:&errorInfo])
                NSLog(@"Could not compile \"%@\"", source);
            [a addObject:as];
            [as release];
        }
        compiledScripts = [[NSArray alloc] initWithArray:a];
    }
}


- (void)dealloc
{
    [compiledScripts release];
    [super dealloc];
}


- (void)didSelect
{
    if (![self checkUniversalAccess]) {
        if (![[[prefsController content] objectForKey:@"SuppressUniversalAccessAlert"] boolValue]) {
            NSAlert *alert = [[[NSAlert alloc] init] autorelease];
            [alert setMessageText:NSLocalizedString(@"Enable access for assistive devices",
                                                    @"Alert message")];
            [alert setInformativeText:NSLocalizedString(@"Alternate Moves will no work unless access for assistive "
                                                        "is enabled. You can enable it now or in the Universal Access "
                                                        "preference pane (this requires administrator privileges).",
                                                        @"Alert informational text")];
            [alert addButtonWithTitle:NSLocalizedString(@"Enable assistive devices", @"Button text")];
            [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Button text")];
            if ([alert respondsToSelector:@selector(setShowsSuppressionButton:)])
                [alert setShowsSuppressionButton:YES];
            [alert beginSheetModalForWindow:[[self mainView] window]
                              modalDelegate:self
                             didEndSelector:@selector(alertSheetDidEnd:returnCode:contextInfo:)
                                contextInfo:nil];
        } else {
            [[universalAccessWarningImage superview] setHidden:NO];
        }
    }
}


- (void)alertSheetDidEnd:(NSAlert*)alert returnCode:(int)rc contextInfo:(void*)ctx
{
    if ([alert respondsToSelector:@selector(suppressionButton)]
        && [[alert suppressionButton] state] == NSOnState) {
        NSMutableDictionary *prefs = [prefsController content];
        [prefs setObject:[NSNumber numberWithBool:YES] forKey:@"SuppressUniversalAccessAlert"];
        [[NSUserDefaults standardUserDefaults] setPersistentDomain:prefs forName:kDomainName];
    }

    if (rc == NSAlertFirstButtonReturn) {
        NSDictionary *errorInfo;
        [[compiledScripts objectAtIndex:kEnableUniversalAccessScript] executeAndReturnError:&errorInfo];
    }

    [[universalAccessWarningImage superview] setHidden:[self checkUniversalAccess]];
}


- (void)willSelect
{
    NSMutableDictionary *prefs = [NSMutableDictionary
                                  dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults]
                                                            volatileDomainForName:kDomainName]];
    [prefs addEntriesFromDictionary:[[NSUserDefaults standardUserDefaults]
                                     persistentDomainForName:kDomainName]];
    [prefsController setContent:prefs];

    pingTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
                                                 target:self
                                               selector:@selector(ping:)
                                               userInfo:nil
                                                repeats:YES];
    [loginItemButton setState:[self checkLoginItem] ? NSOnState : NSOffState];
    [pingTimer fire];

    [[universalAccessWarningImage superview] setHidden:YES];
}


- (void)willUnselect
{
    [pingTimer invalidate];
}


- (BOOL)checkLoginItem
{
    NSDictionary *errorInfo;
    return [[[compiledScripts objectAtIndex:kCheckLoginItemScript]
             executeAndReturnError:&errorInfo] booleanValue];
}


- (void)ping:(NSTimer*)timer
{
    id remoteObject = [NSConnection rootProxyForConnectionWithRegisteredName:kDomainName
                                                                     host:nil];
    [remoteObject setProtocolForProxy:@protocol(AlternateMovesRemoteProtocol)];

    NSString * bundleNameAndVersion = [[self remoteObject] bundleNameAndVersion];
    BOOL running = (bundleNameAndVersion != nil);

    NSString * status;
    NSString * buttonText;
    SEL action;
    if (!running) {
        status = NSLocalizedString(@"Background process is not running",
                                   @"Background process is not running");
        buttonText = NSLocalizedString(@"Start", @"Start background process button text");
        action = @selector(startAction:);
    } else {
        status = [NSString stringWithFormat:NSLocalizedString(@"%@ is running",
                                                              @"Background process status"),
                  bundleNameAndVersion];
        buttonText = NSLocalizedString(@"Stop", @"Start background process button text");
        action = @selector(stopAction:);
    }

    [backgroundProcessStatus setStringValue:status];
    [startStopButton setAction:action];
    [startStopButton setTitle:buttonText];
}


- (IBAction)preferenceDidChange:(id)sender
{
    [[NSUserDefaults standardUserDefaults] setPersistentDomain:[prefsController content] forName:kDomainName];
    [[NSUserDefaults standardUserDefaults] synchronize];
    [[self remoteObject] synchronizePreferences];
}

- (IBAction)startAction:(id)sender
{
    LSOpenCFURLRef((CFURLRef)[NSURL fileURLWithPath:[self helperPath]], NULL);
    [NSTimer scheduledTimerWithTimeInterval:0.2
                                     target:self
                                   selector:@selector(ping:)
                                   userInfo:nil
                                    repeats:NO];
}

- (IBAction)stopAction:(id)sender
{
    [[self remoteObject] terminate];
    [NSTimer scheduledTimerWithTimeInterval:0.2
                                     target:self
                                   selector:@selector(ping:)
                                   userInfo:nil
                                    repeats:NO];
}

- (IBAction)loginItemAction:(id)sender
{
    NSDictionary *errorInfo;
    NSAppleScript *as;
    if ([sender state] == NSOnState) {
        as = [compiledScripts objectAtIndex:kMakeLoginItemScript];
    } else {
        as = [compiledScripts objectAtIndex:kDeleteLoginItemScript];
    }
    [as executeAndReturnError:&errorInfo];
    [loginItemButton setState:[self checkLoginItem] ? NSOnState : NSOffState];
}


- (IBAction)useFactoryDefaults:(id)sender
{
    NSDictionary *defaults = [[NSUserDefaults standardUserDefaults] volatileDomainForName:kDomainName];
    [prefsController setContent:[NSMutableDictionary dictionaryWithDictionary:defaults]];

    [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:kDomainName];
    [[NSUserDefaults standardUserDefaults] synchronize];
    [[self remoteObject] synchronizePreferences];
}


- (id)remoteObject
{
    id obj = [NSConnection rootProxyForConnectionWithRegisteredName:kDomainName host:nil];
    [obj setProtocolForProxy:@protocol(AlternateMovesRemoteProtocol)];
    return obj;
}


- (NSString*)helperPath
{
    return [[self bundle] pathForResource:@"AlternateMovesHelper" ofType:@"app"];
}


- (BOOL)checkUniversalAccess
{
    NSDictionary *errorInfo;
    return [[[compiledScripts objectAtIndex:kCheckUniversalAccessScript]
             executeAndReturnError:&errorInfo] booleanValue];
}


@end
